import math
import random
import Source.precondition as pre
import Source.chapter2 as c2


def isPalindrome(num: int) -> bool:
    return str(num) == reverseNumber(num=num, display=False)


def isMasonPrime(num: int):
    pre.checkType(num, int, "the data you input must be integer")
    pre.checkValue(num >= 0, "the data you input cannot be negative")

    result = math.log2(num + 1)
    return result.is_integer(), result


def sumOfFactors():
    """
    33
    """

    def getFactor(_num):
        if _num == 1 or _num == -1:
            return _num
        for i in range(2, _num):
            if _num % i == 0:
                return i
        return _num

    num = eval(input("请输入一个正整数："))
    pre.checkType(num, int, "the data you input must be integer")
    pre.checkValue(num > 0, "the data you input must be a positive number")

    sum = 0
    while num != 1:
        factor = getFactor(num)
        num //= factor
        sum += factor

    print("因子之和为：%d" % (sum + 1))


def reverseNumber(**kwargs) -> int:
    """
    34
    @:param: kwargs: num->int
    """
    if "num" not in kwargs:
        num = eval(input("请输入一个整数："))
    else:
        num = kwargs.get("num")

    pre.checkType(num, int, "the data you input must be integer")

    positive = num > 0
    content = str(num) if positive else str(abs(num))
    content = content[::-1]
    if len(content) != 0:
        content = content.lstrip("0")
    num = int(content)
    if not positive:
        num -= num
    if kwargs.get("display", True):
        print(num)
    return num


def getAntiPrimes(**kwargs):
    """
    35
    :param kwargs: count->int
    """
    count = kwargs.get("count", 30)

    def __getAntiPrimes():
        num = 2
        nonlocal count
        while count != 0:
            if (not isPalindrome(num)) and c2.isPrime(num) and c2.isPrime(reverseNumber(num=num, display=False)):
                count -= 1
                yield num
            num += 1

    nums = list(__getAntiPrimes())
    for i in range(0, len(nums), 8):
        print(" ".join("{:>5}".format(num) for num in nums[i:i + 8]))


def findMasonPrimes(**kwargs):
    """
    36
    """
    limit = kwargs.get("limit", 1000)

    pre.checkType(limit, int, "the param 'limit' must be integer")
    pre.checkValue(limit > 0, "the param 'data' must be a positive number")

    for i in range(0, limit + 1):
        valid, p = isMasonPrime(i)
        if valid:
            print("{:>3}{:>4}".format(int(p), i))


def encrypt(**kwargs):
    """
    37
    """
    content = kwargs.get("content", "avbV125av1")
    n = kwargs.get("n", 5)

    pre.checkType(content, str, "the param content must be string!")
    pre.checkType(n, int, "the param content must be int!")

    charList = []
    strNum = ""

    diff = ord('Z') - ord('A') - 1

    for i in content:
        if ord("0") <= ord(i) <= ord("9"):
            strNum += i
        else:
            if len(strNum) != 0:
                num = int(strNum)
                num *= n
                charList.extend(str(num))
                strNum = ""

            i = ord(i)
            isLow = ord('a') <= i <= ord('z')
            i += n
            while not ((isLow and i <= ord('z')) or (not isLow and ord('A') <= i <= ord('Z'))):
                if isLow:
                    i = i - ord('z') + ord('a') - 1
                else:
                    i = i - ord('Z') + ord('A') - 1

            charList.append(chr(i))

    result = "".join(charList)
    if len(strNum) != 0:
        result += str(int(strNum) * n)
    print(result)


def reverseSentence(**kwargs):
    """
    38
    """
    if "sentence" in kwargs:
        sentence = kwargs["sentence"]
    else:
        sentence = input("请输入句子：")

    words = sentence.split()
    words.reverse()
    print("翻转后的英语句子为：%s" % " ".join(words))


def fibonacci(**kwargs):
    """
    39
    """
    if "n" in kwargs:
        n = kwargs["n"]
    else:
        n = eval(input("请输入第n项"))

    pre.checkType(n, int, "the data you input must be integer!")
    pre.checkValue(n > 0, "the data you input must bo positive!")

    def _f(_n: int) -> int:
        if _n == 1 or _n == 2:
            return 1
        else:
            return _f(_n-1) + _f(_n-2)

    print(_f(n))